﻿using System.Globalization;
using System.Text.RegularExpressions;
using Oxage.Common;
using Oxage.Windows.Controls;

namespace Oxage.Models
{
	public partial class AxisViewModel : ViewModelBase
	{
		#region Constructor
		public AxisViewModel()
		{
			base.PropertyChanged += (sender, e) =>
			{
				switch (e.PropertyName)
				{
					case "TargetValue":
					case "CurrentValue":
						OnPropertyChanged("MovementDescription");
						OnPropertyChanged("TargetValueFormatted");
						OnPropertyChanged("TargetValueSign");
						break;

					case "Name":
						OnPropertyChanged("MoveButtonName");
						break;
				}
			};

			//Default values
			this.Name = "?";
			this.AxisMovement = AxisMovement.Absolute;
			this.AxisType = AxisType.Linear;
		}
		#endregion

		#region Public properties
		/// <summary>
		/// Gets or sets name of the axis.
		/// </summary>
		[DataForm(FriendlyName = "Name", GroupName = "Axis")]
		public string Name
		{
			get
			{
				return this.name;
			}
			set
			{
				if (this.name != value)
				{
					this.name = value;
					OnPropertyChanged("Name");
				}
			}
		}
		private string name;

		[DataForm(FriendlyName = "Min limit", GroupName = "Axis")]
		public int MinLimit
		{
			get
			{
				return this.minLimit;
			}
			set
			{
				if (this.minLimit != value)
				{
					this.minLimit = value;
					OnPropertyChanged("MinLimit");
				}
			}
		}
		private int minLimit;

		[DataForm(FriendlyName = "Max limit", GroupName = "Axis")]
		public int MaxLimit
		{
			get
			{
				return this.maxLimit;
			}
			set
			{
				if (this.maxLimit != value)
				{
					this.maxLimit = value;
					OnPropertyChanged("MaxLimit");
				}
			}
		}
		private int maxLimit;

		[DataForm(FriendlyName = "Home position", GroupName = "Axis")]
		public int Home
		{
			get
			{
				return this.home;
			}
			set
			{
				if (this.home != value)
				{
					this.home = value;
					OnPropertyChanged("Home");
				}
			}
		}
		private int home;

		//TODO: Has limit switch
		//TODO: Movement type: linear/angular
		
		public AxisMovement AxisMovement
		{
			get
			{
				return this.axisMovement;
			}
			set
			{
				if (this.axisMovement != value)
				{
					this.axisMovement = value;
					OnPropertyChanged("AxisMovement");
				}
			}
		}
		private AxisMovement axisMovement;

		public AxisType AxisType
		{
			get
			{
				return this.axisType;
			}
			set
			{
				if (this.axisType != value)
				{
					this.axisType = value;
					OnPropertyChanged("AxisType");
				}
			}
		}
		private AxisType axisType;

		public double CurrentValue
		{
			get
			{
				return this.currentValue;
			}
			set
			{
				if (this.currentValue != value)
				{
					this.currentValue = value;
					OnPropertyChanged("CurrentValue");
				}
			}
		}
		private double currentValue;

		/// <summary>
		/// Gets or sets a value to be applied on a server, e.g. absolute target position of axis.
		/// </summary>
		public double TargetValue
		{
			get
			{
				return this.targetValue;
			}
			set
			{
				if (this.targetValue != value)
				{
					this.targetValue = value;
					OnPropertyChanged("TargetValue");
				}
			}
		}
		private double targetValue;
		#endregion

		#region Public properties
		public string MovementDescription
		{
			get
			{
				string result = null;

				switch (this.AxisMovement)
				{
					case AxisMovement.Relative:
						result = string.Format("move {0} from {1} by {2} to {3}", this.Name, this.CurrentValueFormatted, this.TargetValueFormatted, GetFormattedValue(this.CurrentValue + this.TargetValue));
						break;

					case AxisMovement.Absolute:
						result = string.Format("move {0} from {1} to {2}", this.Name, this.CurrentValueFormatted, this.TargetValueFormatted);
						break;
				}

				return result;
			}
		}

		public string CurrentValueFormatted
		{
			get
			{
				return GetFormattedValue(this.CurrentValue);
			}
		}

		public string TargetValueFormatted
		{
			get
			{
				return GetFormattedValue(this.TargetValue);
			}
			set
			{
				if (string.IsNullOrEmpty(value))
				{
					this.TargetValue = 0;
				}
				else
				{
					var match = Regex.Match(value, @"\s*(?<sign>[+-]?)\s*(?<num>[\d,.]+)");
					if (match.Success)
					{
						int sign = (match.Groups["sign"] != null && match.Groups["sign"].Value == "-" ? -1 : +1);
						string num = match.Groups["num"].Value.Replace(",", ".");

						double number = 0;
						if (double.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
						{
							this.TargetValue = number * sign;
						}
						else
						{
							this.TargetValue = 0;
						}
					}
				}

				OnPropertyChanged("TargetValueFormatted");
				OnPropertyChanged("TargetValueSign");
				OnPropertyChanged("TargetValue");
			}
		}

		public string MoveButtonName
		{
			get
			{
				return string.Format("Move {0}", this.Name);
			}
		}

		public bool InProgress
		{
			get
			{
				return this.inProgress;
			}
			set
			{
				if (this.inProgress != value)
				{
					this.inProgress = value;
					OnPropertyChanged("InProgress");
					OnPropertyChanged("CanClick");
				}
			}
		}
		private bool inProgress;

		public bool CanClick
		{
			get
			{
				return !this.InProgress;
			}
		}
		#endregion

		#region Protected methods
		protected string GetFormattedValue(double value)
		{
			string sign = (value > 0 ? "+" : ""); //Explicitly display + for positive numbers
			string units = (this.AxisType == AxisType.Angular ? "°" : ""); //Unit of the value
			return string.Format("{0}{1}{2}", sign, value.ToString("N2", CultureInfo.InvariantCulture), units);
		}
		#endregion

		#region Public methods
		public void ToggleTargetValueSign()
		{
			this.TargetValue *= -1;
		}
		#endregion
	}
}
